﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Simulator;
using Statistics;
using ZedGraph;


namespace ModelConfigurator
{
    public partial class FormSimulation : FormBase
    {
        /// <summary>
        /// Имитационная модель
        /// </summary>
        public SimModel Sim {get;set;}
        /// <summary>
        /// Список всех категорий ресурсов
        /// </summary>
        public List<string> Categories { get; set; }
        /// <summary>
        /// Список всех блоков ресурсов
        /// </summary>
        public List<Resource> Res { get; set; }

        /// <summary>
        /// переменная, отвечающая за работу потока, в котором запескается ИМ
        /// </summary>
        private PointsForReport pfr;

        private int nExp;//число экспериментов


        public FormSimulation()
        {
            InitializeComponent();
            nExp = 0;
        }

        private void FormSimulation_Load(object sender, EventArgs e)
        {
            //добавим картинки в три-вью
            ImageList il = new ImageList();
            il.Images.Add(ModelConfigurator.Properties.Resources.small_folder);
            il.Images.Add(ModelConfigurator.Properties.Resources.checkbox_no);
            il.Images.Add(ModelConfigurator.Properties.Resources.checkbox_yes);
            treeViewRes.ImageList = il;
            //загрузим список в три-вью 
            TreeNode nodeCat;
            TreeNode nodeBlock;
            
            //для узлов категорий - у них уникально название (текст)
            TreeNode[] tn = new TreeNode[Categories.Count];
            for (int i = 0; i < Categories.Count; i++)
            {
                if (Sim.Categories.Contains(Categories[i]))//если такая категория содержится в Sim
                {
                    tn[i] = new TreeNode(Categories[i], 2, 2);
                    tn[i].Checked = true;
                }
                else
                {
                    tn[i] = new TreeNode(Categories[i], 1, 1);
                    tn[i].Checked = false;
                }
                
            }
            nodeCat = new TreeNode("Категории ресурсов", 0, 0, tn);//узел корня
            nodeCat.Name = "NodeCat";

            //для узлов "блок ресурсов" 
            tn = new TreeNode[Res.Count];
            for (int i = 0; i < Res.Count; i++)
            {
                if (Sim.Res.Contains(Res[i]))//если такой блок ресурса содержится в Sim
                {
                    tn[i] = new TreeNode(Res[i].Text, 2, 2);
                    tn[i].Checked = true;
                }
                else
                {
                    tn[i] = new TreeNode(Res[i].Text, 1, 1);
                    tn[i].Checked = false;
                }
                // у блоков "Ресурс" уникально имя, текст может повторяться
                tn[i].Name = Res[i].Name;
            }
            nodeBlock = new TreeNode("Блоки ресурсов", 0, 0, tn);
            nodeBlock.Name = "NodeBlock";
            treeViewRes.Nodes.Add(nodeCat);
            treeViewRes.Nodes.Add(nodeBlock);

            //настроим время
            numericUpDownTime.Value = Sim.SimulationTime;
            switch (Sim.SimulationTimeUnits)
            {
                case TimeUnits.days:
                    comboBoxTime.SelectedItem = comboBoxTime.Items[0];
                    break;
                case TimeUnits.hours:
                    comboBoxTime.SelectedItem = comboBoxTime.Items[1];
                    break;
                case TimeUnits.minutes:
                    comboBoxTime.SelectedItem = comboBoxTime.Items[2];
                    break;
                default:
                    comboBoxTime.SelectedItem = comboBoxTime.Items[3];
                    break;
            }

            //настроим метку %

            labelPercent.Text = "";
        }

        private void treeViewRes_AfterSelect(object sender, TreeViewEventArgs e)
        {

        }

        /// <summary>
        /// Изменить галочку и выбрать/убрать элемент дерева
        /// </summary>
        /// <param name="e"></param>
        private void ChangeCheck(TreeNodeMouseClickEventArgs e)
        {
            if (e.Node.Parent != null)
            {
                //изменим свойство "чекед"
                if (e.Node.IsSelected)
                    e.Node.Checked = !e.Node.Checked;
                if (e.Node.Checked)
                {
                    //изменим картинку
                    e.Node.ImageIndex = e.Node.SelectedImageIndex = 2;

                    //изменим список элементов модели, по кот. собирается статистика
                    if (e.Node.Parent.Name == "NodeBlock")//тут ищем по уникальным именам
                    {
                        Resource rc = Res.Find(delegate(Resource r) { return (r.Name == e.Node.Name); });
                        if(!Sim.Res.Contains(rc))//если такого ресурса в списке стат. нет, добавим
                            Sim.Res.Add(rc);
                    }
                    if (e.Node.Parent.Name == "NodeCat")//тут и название (текст) уникальны, ищем по ним
                    {
                        string ct = Categories.Find(delegate(string s) { return (s == e.Node.Text); });
                        if(!Sim.Categories.Contains(ct))
                            Sim.Categories.Add(ct);
                    }
                }
                else
                {
                    e.Node.ImageIndex = e.Node.SelectedImageIndex = 1;
                    //изменим список элементов модели, по кот. собирается статистика
                    if (e.Node.Parent.Name == "NodeBlock")//тут ищем по уникальным именам
                    {
                        Resource rc = Res.Find(delegate(Resource r) { return (r.Name == e.Node.Name); });
                        if (Sim.Res.Contains(rc))
                            Sim.Res.Remove(rc);
                    }
                    if (e.Node.Parent.Name == "NodeCat")//тут и название (текст) уникальны, ищем по ним
                    {
                        string sc = Categories.Find(delegate(string s) { return (s == e.Node.Text); });
                        if (Sim.Categories.Contains(sc))
                            Sim.Categories.Remove(sc);
                    }
                }
            }
        }

        private void treeViewRes_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            ChangeCheck(e);
        }

        private void treeViewRes_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            ChangeCheck(e);
        }

        private void numericUpDownTime_ValueChanged(object sender, EventArgs e)
        {
            //настроим время моделирования
            Sim.SimulationTime = numericUpDownTime.Value;
        }

        private void comboBoxTime_SelectedIndexChanged(object sender, EventArgs e)
        {
            //настроим единицы измерения времени
            switch (comboBoxTime.SelectedIndex)
            {
                case 0:
                    Sim.SimulationTimeUnits = TimeUnits.days;
                    break;
                case 1:
                    Sim.SimulationTimeUnits = TimeUnits.hours;
                    break;
                case 2:
                    Sim.SimulationTimeUnits = TimeUnits.minutes;
                    break;
                default:
                    Sim.SimulationTimeUnits = TimeUnits.seconds;
                    break;
            }
        }

        private void buttonOK_Click(object sender, EventArgs e)
        {
            //запуск ИМ.
            Thread runThread = new Thread(RunCycle);
            runThread.Name = "SAPR-Sim: Simulating";

            buttonOK.Enabled = false;
            buttonStop.Enabled = true;

            //по каждому циклу ИМ будем обновлять статус-бар
            Sim.OnNotification = UpdateStatusBar;

            runThread.Start();//запустим поток
        }

        private void RunCycle()
        {
            pfr = Sim.Run();
            //функция выполнилась, отобразим результаты
            ShowResults();
            //по окончании ИМ прервем текущий поток
            Thread.CurrentThread.Interrupt();
        }

        delegate void SetPercents(int Percents);
        /// <summary>
        /// Обновить статус-бар
        /// </summary>
        private void UpdateStatusBar()
        {
            int Percents = Convert.ToInt32(Sim.SysTime * 100 / Sim.SimulationTimeInSeconds);
            SetPercents sbv = SetStatusBarValue;
            this.Invoke(sbv, Percents);
        }

        private void SetStatusBarValue(int Percents)
        {
            
            if (Percents > 100)//если так, то все закончилось
                Percents = 100;
            Bar1.Value = Percents;
            labelPercent.Text = "Выполнено: " + Percents + "% ";
        }

        /// <summary>
        /// Делегат для того, чтобы записать в textBoxResults результаты моделирования и вызвать формы с графиками
        /// </summary>
        private delegate void ShowSimResults();

        /// <summary>
        /// Записать в textBoxResults результаты моделирования и вызвать формы с графиками
        /// </summary>
        private void ShowResults()
        {
            ShowSimResults ssr = ShowSimulationResoults;
            this.Invoke(ssr);
        }

        private void ShowSimulationResoults()
        {
            //выведем единичные точки статистики

            textBoxResults.AppendText("Эксперимент № "+ (++nExp) + "\n");
            for (int i = 0; i < pfr.SinglePoints.Count; i++)
            {
                string txt = i + ") " + pfr.SinglePointNames[i] + " " + Math.Round(pfr.SinglePoints[i],3) + "\n";
                textBoxResults.AppendText(txt);
            }

            buttonOK.Enabled = true;

            buttonStop.Enabled = false;

            Bar1.Value = 0;
            labelPercent.Text = "";

            //выведем формы
            FormGraph fg;
            //выведем форму для ресурсов
            fg = new FormGraph("Загруженность ресурсов по блокам", Sim.SimulationTimeUnits, null);
            fg.PointList = pfr.GetPointListOfOneCategory("Resources");//экземпляр класса PointForReports со 
                         //списком статистики по одному типу данных, а именно, "Resources"
            fg.Text = "График. Эксперимент № " + nExp;
            if (fg.PointList != null)
                fg.Show();

            //выведем форму для категорий
            fg = new FormGraph("Загруженность ресурсов по категориям", Sim.SimulationTimeUnits, null);
            fg.PointList = pfr.GetPointListOfOneCategory("Categories");
            fg.Text = "График. Эксперимент № " + nExp;
            if (fg.PointList != null)
                fg.Show();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            //остановим имитационную модель
            Sim.Abort();
            buttonStop.Enabled = false;
            buttonOK.Enabled = true;
        }

        private void buttonHelp_Click(object sender, EventArgs e)
        {
            string openCHMFile = Application.StartupPath + "\\Справка SAPR-SIM.chm";
            try
            {
                if (!System.IO.File.Exists(openCHMFile))
                    System.IO.File.WriteAllBytes(openCHMFile, global::ModelConfigurator.Properties.Resources.Справка_SAPR_SIM);
            }
            catch
            {
                //если файл есть он не перепишется
            }
            helpProvider1.HelpNamespace = openCHMFile;
            Help.ShowHelp(this, this.helpProvider1.HelpNamespace, HelpNavigator.Topic, "module_2_1.htm");
        }
    }
}
